﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

namespace Jackletter.BlendAuth
{
    public class BlendAuthenticationHandler : AuthenticationHandler<BlendAuthenticationOptions>
    {
        private readonly ChunkingCookieManager chunkManager = new ChunkingCookieManager();
        private readonly ITokenManager tokenManager;

        public BlendAuthenticationHandler(IOptionsMonitor<BlendAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, ITokenManager tokenManager) : base(options, logger, encoder, clock)
        {
            this.tokenManager = tokenManager;
        }
        protected new BlendAuthenticationEvents Events
        {
            get => (BlendAuthenticationEvents)base.Events;
            set => base.Events = value;
        }

        protected override Task<object> CreateEventsAsync() => Task.FromResult<object>(new BlendAuthenticationEvents());

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            string token = ReadToken();
            if (string.IsNullOrWhiteSpace(token))
            {
                return AuthenticateResult.NoResult();
            }
            await Events.ValidateToken(token, Context);
            var principal = GetPrincipal(token);
            if (principal == null)
            {
                return AuthenticateResult.Fail("未登录或已过期");
            }
            return AuthenticateResult.Success(new AuthenticationTicket(principal, Scheme.Name));
        }

        private ClaimsPrincipal GetPrincipal(string token)
        {
            var user = tokenManager.GetUser(token);
            if (user == null) return null;
            var claimUserId = new Claim(ClaimTypes.NameIdentifier, user.UserId.ToString());
            var claimUserToken = new Claim("_token", token);
            var identity = new ClaimsIdentity(new Claim[] { claimUserId, claimUserToken }, "Blend");
            ClaimsPrincipal principal = new ClaimsPrincipal(identity);

            return principal;
        }

        private string ReadToken()
        {
            string token = string.Empty;
            //1.先从cookie中获取
            if (Options.EnableTokenInCookie) token = chunkManager.GetRequestCookie(Context, Options.TokenInCookieName);

            //2.再从header中获取
            if (string.IsNullOrWhiteSpace(token))
            {
                if (Options.EnableTokenInHeader) token = Context.Request.Headers[Options.TokenInHeaderName];
                if (!string.IsNullOrWhiteSpace(token))
                {
                    if (token.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
                    {
                        token = token.Substring("Bearer ".Length).Trim();
                    }
                }
            }
            //3.最后从url中获取
            if (string.IsNullOrWhiteSpace(token))
            {
                if (Options.EnableTokenInUrl) token = chunkManager.GetRequestCookie(Context, Options.TokenInUrlName);
            }
            return token;
        }

        protected override Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            Response.StatusCode = 401;
            return Task.CompletedTask;
        }

        protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
        {
            Response.StatusCode = 403;
            return Task.CompletedTask;
        }
    }
}